/*
 * tclass.c
 *
 *  Created on: Jan 24, 2016
 *      Author: root
 */

#include <string.h>
#include "../basic/operator.h"



/*
Basic types hierarchy:

-Object
  -Class
  -Atom
    -Boolean
    -Number
      -Real
        -Decimal
        -Fraction
        -Integer
      -Complex
    -Character
    -String
    -Symbol
      -Syntax
  	  -Property
  -Vector
  -Environment
  -Procedure
    -Closure
    -Continuation
    -Method
    -Constructor
  -Exception
  -Pair
    -Promise
 * */

void init_class_hierarchy(Scheme *sc) {
	sc->inheritance = init_tree();
	tree_add(sc->inheritance, NULL, OBJECT_TYPE, &g_nil);

    tree_add(sc->inheritance, OBJECT_TYPE, CLASS_TYPE, &g_nil);
    tree_add(sc->inheritance, OBJECT_TYPE, ATOM_TYPE, &g_nil);
    tree_add(sc->inheritance, OBJECT_TYPE, VECTOR_TYPE, &g_nil);
    tree_add(sc->inheritance, OBJECT_TYPE, ENVIRONMENT_TYPE, &g_nil);
    tree_add(sc->inheritance, OBJECT_TYPE, PROCEDURE_TYPE, &g_nil);
    tree_add(sc->inheritance, OBJECT_TYPE, EXCEPTION_TYPE, &g_nil);
    tree_add(sc->inheritance, OBJECT_TYPE, PAIR_TYPE, &g_nil);

    tree_add(sc->inheritance, ATOM_TYPE, BOOLEAN_TYPE, &g_nil);
    tree_add(sc->inheritance, ATOM_TYPE, NUMBER_TYPE, &g_nil);
    tree_add(sc->inheritance, ATOM_TYPE, CHARACTER_TYPE, &g_nil);
    tree_add(sc->inheritance, ATOM_TYPE, STRING_TYPE, &g_nil);
    tree_add(sc->inheritance, ATOM_TYPE, SYMBOL_TYPE, &g_nil);

    tree_add(sc->inheritance, NUMBER_TYPE, COMPLEX_TYPE, &g_nil);
    tree_add(sc->inheritance, NUMBER_TYPE, REAL_TYPE, &g_nil);
    tree_add(sc->inheritance, REAL_TYPE, DECIMAL_TYPE, &g_nil);
    tree_add(sc->inheritance, REAL_TYPE, FRACTION_TYPE, &g_nil);
    tree_add(sc->inheritance, REAL_TYPE, INTEGER_TYPE, &g_nil);

    tree_add(sc->inheritance, SYMBOL_TYPE, SYNTAX_TYPE, &g_nil);

    tree_add(sc->inheritance, PROCEDURE_TYPE, CLOSURE_TYPE, &g_nil);
    tree_add(sc->inheritance, PROCEDURE_TYPE, CONTINUATION_TYPE, &g_nil);
    tree_add(sc->inheritance, PROCEDURE_TYPE, METHOD_TYPE, &g_nil);

    tree_add(sc->inheritance, PAIR_TYPE, PROMISE_TYPE, &g_nil);
}

static void add_class_by_name(Scheme *sc, const char *name, Cell *clazz) {
	Cell* symbol = make_symbol(sc, name);
	Cell* c = find_slot_by_symbol(sc, sc->globalObjects, symbol);
	if (c != &g_nil) {
		fprintf(stderr, "Class %s already defined!\n", symbol_name(symbol));
		return;
	}
	add_slot_by_symbol(sc, sc->globalObjects, symbol, clazz);
}

// class ds: (className (properties ...) (constructors ...) (methods ...))
// property: (propertyName propertyType)
// constructor: (constructorName constructorDef)
// method: (methodName methodDef)

static void add_property_to_class(Scheme *sc, Cell *clazz, Cell *property) {
	add_slot_by_symbol(sc, second(clazz), car(property), cdr(property));
}

//static void add_constructor_to_class(Scheme *sc, Cell *clazz, Cell *constructor) {
//	add_slot_by_symbol(sc, third(clazz), car(constructor), cdr(constructor));
//}

static void add_method_to_class(Scheme *sc, Cell *clazz, Cell *method) {
	add_slot_by_symbol(sc, fourth(clazz), car(method), cdr(method));
}

static void add_this_op(Scheme *sc, Cell *args, Cell *code) {
	Cell *element = NULL;
	Cell *ele = NULL;
	for (element = code; element != &g_nil; element = cdr(element)) {
		ele = car(element);
		if (type_of(sc, ele, PAIR_TYPE)) {
			if (!eqv(sc, car(ele), cell_symbol_this))
				add_this_op(sc, args, ele);
		} else {
			if (!cell_contains_element(sc, args, ele)) {
				CAR(element) = cons(sc, cell_symbol_this, ele);
			}
		}
	}
}

// args: ()
// code: (* *PI* radias radias)
// (lambda (this) (* *PI* (this radias) (this radias)))
static Cell* get_method_closure(Scheme *sc, Cell *args, Cell *code) {
	Cell *new_args = cons(sc, cell_symbol_this, args);
	add_this_op(sc, args, code);
	return cons(sc, cell_symbol_lambda, cons(sc, new_args, cons(sc, code, &g_nil)));
}

int inherit_from(Scheme *sc, const char *child, const char *parent) {
	TreeNode *node = tree_find(sc->inheritance, child);
	TreeNode *tnode = node;
	while (tnode != NULL) {
		if (strcmp(parent, tnode->name) == 0)
			return TRUE;
		tnode = tnode->parent;
	}
	return FALSE;
}

// A is a B
int is_a(Scheme *sc, Cell *a, Cell *b) {
	return inherit_from(sc, a->_type, b->_type);
}


//////////////////////////

Cell *find_class_by_name(Scheme *sc, Cell *className) {
	return find_slot_by_symbol(sc, sc->globalObjects, className);
}

Cell *find_property_of_class(Scheme *sc, Cell *clazz, Cell *property) {
	Cell *classDef = find_class_by_name(sc, clazz);
	Cell *propertyDef = find_slot_by_symbol(sc, second(classDef), property);
	return propertyDef;
}

Cell *find_constructor_of_class(Scheme *sc, Cell *clazz, Cell *constructor) {
	Cell *classDef = find_class_by_name(sc, clazz);
	Cell *constructorDef = find_slot_by_symbol(sc, third(classDef), constructor);
	return constructorDef;
}

Cell *find_method_of_class(Scheme *sc, Cell *clazz, Cell *method) {
	Cell *classDef = find_class_by_name(sc, clazz);
	Cell *methodDef = find_slot_by_symbol(sc, fourth(classDef), method);
	return methodDef;
}

Cell *make_class(Scheme *sc, Cell *className) {
	Cell *properties = make_vector(sc, PROPERTY_COUNT);
	// Cell *constructors = make_vector(sc, CONSTRUCTOR_COUNT);
	Cell *methods = make_vector(sc, METHOD_COUNT);

	Cell *clazz = cons(sc, methods, &g_nil);
	// clazz = cons(sc, constructors, clazz);
	clazz = cons(sc, properties, clazz);
	clazz = cons(sc, className, clazz);
	clazz->_type = CLASS_TYPE;

	add_class_by_name(sc, symbol_name(className), clazz);
	tree_add(sc->inheritance, OBJECT_TYPE, symbol_name(className), &g_nil);
	return clazz;
}

// --(define Class Circle (radias Number) (make-circle) (draw move area))
/*
Cell* define_class(Scheme *sc, Cell *code) {
	Cell *className = first(code);
	Cell *properties = second(code);
	Cell *constructors = third(code);
	Cell *methods = fourth(code);

	Cell *clazz = make_class(sc, className);
	Cell *propertyName = NULL;
	Cell *propertyType = NULL;
	for ( ; properties != &g_nil; properties = cddr(properties)) {
		propertyName = first(properties);
		propertyType = second(properties);
		add_property_to_class(sc, clazz, cons(sc, propertyName, propertyType));
	}

	Cell *constructor = NULL;
	for ( ; constructors != &g_nil; constructors = cdr(constructors)) {
		constructor = cons(sc, first(constructors), &g_nil);
		constructor->_type = CONSTRUCTOR_TYPE;
		add_constructor_to_class(sc, clazz, constructor); //constructor value? -- define Constructor     constructor signature
	}

	Cell *method = NULL;
	for ( ; methods != &g_nil; methods = cdr(methods)) {
		method = cons(sc, first(methods), &g_nil);
		method->_type = METHOD_TYPE;
		add_method_to_class(sc, clazz, method); //method value? -- define Method     method signature
	}

	return clazz;
}
*/

// (define-class className)
Cell* op_define_class(Scheme *sc) {
	Cell *className = first(sc->args);
	if (find_class_by_name(sc, className) != &g_nil) {
		sc->returnValue = make_default_exception_1(sc, "CLASS_EXISTED", "Cannot add because class [%s] already existed.", symbol_name(className));
		return pop_callstack(sc);
	}
	make_class(sc, className);
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}

// (delete-class className)
Cell* op_delete_class(Scheme *sc) {
	PRIVILEGE_CHECK(sc);
	Cell *className = first(sc->args);
	if (find_class_by_name(sc, className) == &g_nil) {
		sc->returnValue = make_default_exception_1(sc, "CLASS_NOT_EXISTED", "Cannot delete because class [%s] not existed.", symbol_name(className));
		return pop_callstack(sc);
	}
	if (!is_queue_empty(tree_find(sc->inheritance, symbol_name(className))->children)) {
		sc->returnValue = make_default_exception_1(sc, "CLASS_BE_INHERITED", "Cannot delete because class [%s] has been inherited.", symbol_name(className));
		return pop_callstack(sc);
	}
	//tree_delete(sc->inheritance, symbol_name(className), &g_nil);
	//delete_class_by_name(sc, symbol_name(className));
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}

// (rename-class oldName newName) ??????
Cell* op_rename_class(Scheme *sc) {
	PRIVILEGE_CHECK(sc);
	//Cell *oldName = first(sc->args);
	//Cell *newName = second(sc->args);
	//tree_replace(sc->inheritance, symbol_name(oldName), symbol_name(newName));
	//replace_symbol(oldName, newName);
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}

// (find-class className)
Cell* op_find_class(Scheme *sc) {
	PRIVILEGE_CHECK(sc);
	Cell *className = first(sc->args);
	//regular expression?
	Cell *clazz = find_class_by_name(sc, className);
	sc->returnValue = clazz;
	return pop_callstack(sc);
}

// (add-property Circle radius Number)
// -- (add-properties Circle (location Location border Number))
// {define macro {add properties ...} as {...}}
Cell* op_add_property(Scheme *sc) {
	Cell *className = first(sc->args);
	Cell *propertyName = second(sc->args);
	Cell *propertyType = third(sc->args);
	Cell* clazz = find_class_by_name(sc, className);
	Cell* property = find_property_of_class(sc, clazz, propertyName);
	if (clazz == &g_nil) {
		sc->returnValue = make_default_exception_1(sc, "CLASS_NOT_EXISTED", "Cannot add property because class [%s] not existed.", symbol_name(className));
		return pop_callstack(sc);
	}
	if (property != &g_nil) {
		sc->returnValue = make_default_exception_1(sc, "PROPERTY_EXISTED", "Cannot add property because property [%s] already existed.", symbol_name(propertyName));
		return pop_callstack(sc);
	}
	add_property_to_class(sc, clazz, cons(sc, propertyName, propertyType));
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}

// (delete-property Circle radius)
// --(delete-properties Circle (location border))
// {define macro {delete properties ...} as {...}}
Cell* op_delete_property(Scheme *sc) {
	PRIVILEGE_CHECK(sc);
	Cell *className = first(sc->args);
	Cell *propertyName = second(sc->args);
	Cell* clazz = find_class_by_name(sc, className);
	Cell* property = find_property_of_class(sc, clazz, propertyName);
	if (clazz == &g_nil) {
		sc->returnValue = make_default_exception_1(sc, "CLASS_NOT_EXISTED", "Cannot delete property because class [%s] not existed.", symbol_name(className));
		return pop_callstack(sc);
	}
	if (property == &g_nil) {
		sc->returnValue = make_default_exception_1(sc, "PROPERTY_NOT_EXISTED", "Cannot delete property because property [%s] not existed.", symbol_name(propertyName));
		return pop_callstack(sc);
	}
	//delete_property_of_class(sc, propertyName);
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}

// (add-method Circle area (radius))
// --(add-methods Circle (method1 method1Args method2 method2Args))
// {define macro {add methods ...} as {...}}
Cell* op_add_method(Scheme *sc) {
	Cell *className = first(sc->args);
	Cell* clazz = find_class_by_name(sc, className);
	Cell *methodName = second(sc->args);
	Cell *methodArgs = third(sc->args);
	Cell *method = cons(sc, methodName, methodArgs);
	method->_type = METHOD_TYPE;
	if (clazz == &g_nil) {
		sc->returnValue = make_default_exception_1(sc, "CLASS_NOT_EXISTED", "Cannot add method because class [%s] not existed.", symbol_name(className));
		return pop_callstack(sc);
	}
	if (find_method_of_class(sc, clazz, methodName) != &g_nil) {
		sc->returnValue = make_default_exception_1(sc, "METHOD_EXISTED", "Cannot add method because method [%s] already existed.", symbol_name(methodName));
		return pop_callstack(sc);
	}
	add_method_to_class(sc, clazz, method); //method value? -- define Method
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}

// (delete-method Circle area)
// --(delete-methods Circle (area draw))
// {define macro {delete methods ...} as {...}}
Cell* op_delete_method(Scheme *sc) {
	PRIVILEGE_CHECK(sc);
	Cell *className = first(sc->args);
	Cell *methodName = second(sc->args);
	Cell* clazz = find_class_by_name(sc, className);
	Cell* method = find_method_of_class(sc, clazz, methodName);
	if (clazz == &g_nil) {
		sc->returnValue = make_default_exception_1(sc, "CLASS_NOT_EXISTED", "Cannot delete method because class [%s] not existed.", symbol_name(className));
		return pop_callstack(sc);
	}
	if (method == &g_nil) {
		sc->returnValue = make_default_exception_1(sc, "PROPERTY_NOT_EXISTED", "Cannot delete method because method [%s] not existed.", symbol_name(methodName));
		return pop_callstack(sc);
	}
	//delete_method_of_class(sc, methodName);
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}

// (rename-property class oldPropName newPropName)
Cell* op_rename_property(Scheme *sc) {
	;
	//
	return pop_callstack(sc);
}

// (rename-method class oldMethodName newMethodName)
Cell* op_rename_method(Scheme *sc) {
	;
	//
	return pop_callstack(sc);
}

Cell* op_change_property_type(Scheme *sc) {
	;
	//
	return pop_callstack(sc);
}




// (define-method area Circle () (* *PI* radias radias))
Cell* define_method(Scheme *sc, Cell *code, int firstDefine) {
	Cell *methodName = first(code);
	Cell *className = second(code);
	Cell *args = third(code);
	Cell *methodCode = fourth(code);

	Cell* clazz = find_class_by_name(sc, className);
	Cell* method = find_method_of_class(sc, clazz, methodName);
	if (clazz == &g_nil) {
		return make_default_exception_1(sc, "CLASS_NOT_EXISTED", "Cannot define method because class [%s] not existed.", symbol_name(className));
	}
	if (method == &g_nil) {
		return make_default_exception_1(sc, "METHOD_NOT_EXISTED", "Cannot define method because method [%s] not existed.", symbol_name(methodName));
	}

	Cell *methodDef = get_method_closure(sc, args, methodCode);
	Cell *slot = find_method_of_class(sc, className, methodName);
	if (firstDefine && cdr(slot) != &g_nil) {
		return make_default_exception_1(sc, "METHOD_DEFINED", "Cannot define method because method [%s] already defined.", symbol_name(methodName));
	}
	CDR(slot) = methodDef;
	return slot;
}

/*
// (define-constructor make-circle Circle (radias) (begin (define obj (new-instance Circle)) (set! (obj radias) radias) obj))
// (lambda (radias) (...))
Cell* define_constructor(Scheme *sc, Cell *code) {
	Cell *constructorName = first(code);
	Cell *className = second(code);
	Cell *args = third(code);
	Cell *constructorCode = fourth(code);

	Cell *constructor = get_method_closure(sc, args, constructorCode);
	Cell *slot = find_constructor_of_class(sc, className, constructorName);
	CDR(slot) = constructor;
	return slot;
}
*/

// (define-method area Circle () (* *PI* radias radias))
Cell* op_define_method(Scheme *sc) {
	sc->returnValue = define_method(sc, cdr(sc->args), TRUE);
	return pop_callstack(sc);
}

Cell* op_redefine_method(Scheme *sc) {
	sc->returnValue = define_method(sc, cdr(sc->args), FALSE);
	return pop_callstack(sc);
}

// redefine constructor?
// redefine function?
// find function?

Cell* op_add_inheritance(Scheme *sc) {
	;
	//
	return pop_callstack(sc);
}

Cell* op_change_inheritance(Scheme *sc) {
	;
	//
	return pop_callstack(sc);
}

// down casting
// (narrow superclass-instance subclass)
Cell* op_narrow(Scheme *sc) {
	;
	//
	return pop_callstack(sc);
}


Cell* op_get_properties(Scheme *sc) {
	//...
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}
Cell* op_get_methods(Scheme *sc) {
	//...
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}
Cell* op_find_property(Scheme *sc) {
	//...
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}
Cell* op_find_method(Scheme *sc) {
	//...
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}



/*
// (add-inheritance Shape Drawable () ())
Cell* op_add_inheritance(Scheme *sc) {
	Cell *child = first(sc->args);
	Cell *parent = second(sc->args);
	Cell *propertiesAndMethods = cddr(sc->args);
	tree_add(sc->inheritance, symbol_name(parent), symbol_name(child), propertiesAndMethods);
	return s_return_helper(sc, &g_true);
}

// (delete-inheritance Shape Drawable)
Cell* op_delete_inheritance(Scheme *sc) {
	//...
	return s_return_helper(sc, &g_true);
}

// --(make-object circle Circle make-circle 10)
// --(make-object d Double make-double 123.456)
// (make-circle (make-object Circle) 10)

// (make-Object 'ClassName)
Cell* op_make_object(Scheme *sc) {
	Cell* className = first(sc->args);
	Cell *object = make_object(className);
	sc->obj = object;
	return s_return_helper(sc, sc->obj);
}

*/



// (new-instance 'ClassName)
Cell* op_new_instance(Scheme *sc) {
	//Cell* className = first(sc->args);
	//Cell *object = make_object(className);
	//sc->obj = object;
	//sc->returnValue = sc->obj;
	return pop_callstack(sc);
}

Cell* op_get_class_name(Scheme *sc) {
	//...
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}

Cell* op_get_superclass_name(Scheme *sc) {
	//...
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}

Cell* op_get_subclass_names(Scheme *sc) {
	//...
	sc->returnValue = &g_true;
	return pop_callstack(sc);
}



void init_class_operators(Scheme *sc) {
	ADD_OPERATOR(sc, "add-property", add_property);
	ADD_OPERATOR(sc, "delete-property", delete_property);
	ADD_OPERATOR(sc, "add-method", add_method);
	ADD_OPERATOR(sc, "delete-method", delete_method);
	ADD_OPERATOR(sc, "add-inheritance", add_inheritance);
	ADD_OPERATOR(sc, "change-inheritance", change_inheritance);
}

